package com.alan.android.sqlite;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import android.util.Log;

import com.alan.android.sqlite.converters.IColumnConverter;
import com.alan.android.sqlite.reflect.ObjectFactory;
import com.alan.android.sqlite.table.Column;
import com.alan.android.sqlite.table.Table;
import com.alan.android.sqlite.table.TableFactory;
import com.alan.android.sqlite.utils.DatabaseHelper;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

public class SqliteManager implements IDatatabseOpertion {

    private AndroidInnerSqlite androidInnerSqlite;
    private SQLiteDatabase sqLiteDatabase;

    private static final String TAG = "SqliteManager";

    private SqliteManager() {
    }

    public static SqliteManager getInstance() {
        return SqliteManagerHolder.sInstance;
    }

    private static class SqliteManagerHolder {
        private static final SqliteManager sInstance = new SqliteManager();
    }

    /**
     * 初始化数据库连接
     * 该方法负责创建并初始化SQLite数据库实例
     * 它通过AndroidInnerSqlite类来管理数据库的创建和版本升级
     *
     * @param context Android上下文，用于获取数据库创建所需的资源
     * @param dbName 数据库名称，用于标识和创建数据库
     * @param classList 一个包含表结构信息的类列表，用于创建数据库表
     * @param version 数据库版本号，用于管理数据库结构的变更
     */
    public void init(Context context, String dbName, List<Class<?>> classList, int version) {
        // 创建AndroidInnerSqlite实例，负责数据库的创建和管理
        androidInnerSqlite = new AndroidInnerSqlite(context, dbName, version, classList);
        // 获取可写的数据库实例，以便后续进行数据库操作
        sqLiteDatabase = androidInnerSqlite.getWritableDatabase();
    }

    @Override
    public <T> T query(String sql, Class<T> clazz) {
        if (null == sqLiteDatabase) {
            Log.w(TAG, "query sqLiteDatabase is null");
            return null;
        }
        try (Cursor cursor = sqLiteDatabase.rawQuery(sql, null)) {
            return getItem(clazz, cursor);
        }
    }

    @Override
    public <T> List<T> queryList(String sql, Class<T> clazz) {
        if (null == sqLiteDatabase) {
            Log.w(TAG, "queryList sqLiteDatabase is null");
            return new ArrayList<>();
        }
        try (Cursor cursor = sqLiteDatabase.rawQuery(sql, null)) {
            return getList(clazz, cursor);
        }
    }

    @Override
    public boolean insert(Object t) {
        if (sqLiteDatabase == null) {
            Log.w(TAG, "insert sqLiteDatabase is null");
            return true;
        }
        ContentValues contentValues = getContentValues(t);
        String tableName = TableFactory.getTableName(t.getClass());
        return sqLiteDatabase.insert(tableName, null, contentValues) > 0;
    }

    @Override
    public boolean replace(Object t) {
        if (sqLiteDatabase == null) {
            Log.w(TAG, "replace sqLiteDatabase is null");
            return true;
        }
        ContentValues contentValues = getContentValues(t);
        String tableName = TableFactory.getTableName(t.getClass());
        return sqLiteDatabase.replace(tableName, null, contentValues) > 0;
    }

    @Override
    public boolean delete(Object o) {
        if (sqLiteDatabase==null){
            Log.w(TAG, "delete sqLiteDatabase is null");
            return false;
        }
        String tableName = TableFactory.getTableName(o.getClass());
        StringBuilder whereClause = new StringBuilder();
        String[] strings = DatabaseHelper.whereIdArgs(o, whereClause);
        int delete = sqLiteDatabase.delete(tableName, whereClause.toString(), strings);
        return delete>0;
    }

    @Override
    public void execute(String sql) {

    }

    private <T> T getItem(Class<T> tClass, Cursor cursor) {
        ObjectFactory<T> factory = new ObjectFactory<>(tClass);
        Table table = TableFactory.getTable(tClass);
        try {
            if (null != cursor && cursor.getCount() > 0 && cursor.moveToFirst()) {
                return getModelByCursor(table, cursor, factory);
            }
            return null;
        } catch (Exception e) {
            Log.e(TAG, Log.getStackTraceString(e));
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return null;
    }

    private static synchronized <T> T getModelByCursor(Table table, Cursor cursor, ObjectFactory<T> objectFactory) throws Exception {
        T t = objectFactory.newInstance();
        Map<String, Column> map = table.getColumns();
        for (int columnIndex = 0, count = cursor.getColumnCount(); columnIndex < count; columnIndex++) {
            String columnName = cursor.getColumnName(columnIndex);
            // 通过字段名字得到对应的java类中的字段
            Column column = map.get(columnName);
            if (column != null) {
                // 得到字段的转化器
                IColumnConverter converter = column.getColumnConverter();
                // 从cursor中得到值
                Object value = converter.getDBType().getValueFromCursor(cursor, columnIndex);
                // 从sql类型的数据转化成java类保存的数据类型
                value = converter.toJavaValue(value);
                // 把value值设置在result实例里
                column.getColumnField().set(t, value);
            }
        }
        return t;
    }

    private <T> List<T> getList(Class<T> tClass, Cursor cursor) {
        ObjectFactory<T> factory = new ObjectFactory<>(tClass);
        Table table = TableFactory.getTable(tClass);
        List<T> list = new ArrayList<>();
        try {
            while (null != cursor && cursor.getCount() > 0 && cursor.moveToNext()) {
                T t = getModelByCursor(table, cursor, factory);
                list.add(t);
            }
        } catch (Exception e) {
            Log.e(TAG, Log.getStackTraceString(e));
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return list;
    }

    private static synchronized ContentValues getContentValues(Object t) {
        ContentValues contentValues = new ContentValues();
        Table table = TableFactory.getTable(t.getClass());
        Collection<Column> values = table.getColumns().values();
        for (Column column : values) {
            String value = DatabaseHelper.getValue(t, column);
            if (column.isAutoIncrement() && (TextUtils.isEmpty(value) || "0".equals(value))) {
                continue;
            }
            contentValues.put(column.getName(), value);
        }
        return contentValues;
    }
}
